import "../env";
import { _decorator, Component, Node, Prefab, instantiate, Camera, Label, TiledUserNodeData, EditBoxComponent, EditBox, Widget, UITransform, ScrollView, Vec2, v2 } from 'cc';
const { ccclass, property } = _decorator;

import { HallClient } from '../../shared/hallClient/HallClient';
import { GameClient } from "../../shared/gameClient/GameClient";
import { FrameSyncExecutor } from "../common/FrameSyncExecutor";
import { PlayerData } from "./PlayerData";
import { PlayerComponent } from "./PlayerComponent";
import { InputType } from "../../shared/gameClient/games/DemoSimpleFrameSyncModels";
import { JoystickComponent } from "../common/ui/JoystickComponent";
import { IMoveDirection } from "../common/ui/BaseJoystick";
import { GameClientFrameSyncConnectAdp } from "../common/GameClientFrameSyncConnectAdp";
import { DemoClient } from "../../shared/demoClient/DemoClient";
import { EMatchFromType, IBaseMeleeMatcherParams, IMatchResult, MatcherKeys } from "../../shared/tsgf/match/Models";
import { delay } from "../../shared/tsgf/Utils";
import { IResult, Result } from "../../shared/tsgf/Result";
import { EFrameSyncState, IRoomInfo } from "../../shared/tsgf/room/IRoomInfo";
import { ERoomMsgRecvType } from "../../shared/tsgf/room/IRoomMsg";
import { EPlayerInputFrameType, IAfterFrames, IFramePlayerInput, IPlayerInputOperate } from "../../shared/tsgf/room/IGameFrame";
import { PlayerNameFlagComponent } from "./PlayerNameFlagComponent";
import { ENetworkState, IPlayerInfo } from "../../shared/tsgf/player/IPlayerInfo";


@ccclass('Demo1SceneManager')
export class Demo1SceneManager extends Component {
    demoClient!: DemoClient;
    hallClient!: HallClient;
    gameClient?: GameClient;


    @property(Node)
    ViewLogin!: Node;
    @property(Node)
    ViewHall!: Node;
    @property(Node)
    ViewRoom!: Node;
    @property(Node)
    ViewRoomGameView!: Node;
    @property(Node)
    ViewRoomBg!: Node;
    @property(Node)
    ViewRoomShowPlayers!: Node;
    @property(Node)
    ViewRoomShowMsg!: Node;
    @property(Node)
    ViewRoomBtnStartGame!: Node;
    @property(Node)
    ViewRoomBtnStopGame!: Node;
    @property(Node)
    ViewGame3D!: Node;

    @property(Node)
    ViewLoading!: Node;
    @property(Label)
    ViewLoadingText!: Label;
    @property(Node)
    ViewTips!: Node;
    @property(Label)
    ViewTipsLabel!: Label;

    @property(Node)
    PlayersContainer?: Node;
    @property({ type: Node, tooltip: "存放所有Flag的UI节点" })
    GameObjFlagUI?: Node;
    @property(Prefab)
    PlayerPrefab?: Prefab;
    @property(Prefab)
    PlayerNameFlagPrefab?: Prefab;

    @property(Camera)
    MainCamera!: Camera;

    @property(JoystickComponent)
    Joystick!: JoystickComponent;

    @property(EditBox)
    PlayerShowName!: EditBox;
    @property(EditBox)
    InpJoinRoomId!: EditBox;
    @property(EditBox)
    ShowRoomId!: EditBox;
    @property(EditBox)
    InpRoomMsg!: EditBox;
    @property(Label)
    ShowRoomPlayers!: Label;
    @property(Label)
    ShowRoomMsg!: Label;
    @property(ScrollView)
    ShowRoomMsgScrollView!: ScrollView;
    @property(Node)
    BtnDismissRoom!: Node;
    @property(Node)
    BtnLeaveRoom!: Node;

    playerShowNameLibs: string[] = ['一块砖', '两百块', '小马哥', '鸭子', 'summer', 'duck', '哎哟跑得快', '江雪↘絮冰', '一表人渣', '风杀云分', '逆天灬小学生', '软软丿丶战', 'Lucky灬宝贝', '战魂英雄劫', '红颜丶空子许', 'Angel_小发丝', '画扇悲风殇月夜', '背叛残局', '残影蚀心', '空城旧梦°你已不在', '冷却的心', '伤城离歌', '村里一朵花~', '曲以终人未散', '划船不用桨全靠浪', '年華已逝ァ', '法丨域丶凌风灬', '留下一道痕', '’情亦茜て', '◤疯子家族∮◥', 'S丶ky丿神之队', 'Giu丶霸气', '傲之', '听弦断丶断那三千的痴缠', '眉眼如初', '暮雪ぅ', '混世小野女♀', '爱迪不能生', '耐法莉安', '怎不戚鹊桥离恨', '温玉琳琅', '唇齿柔情', '傲到龟头里去的男人', '爱过人渣骂过三八', '懂鍀灬舍去丶', '初吻给了香烟', '诗寒无凡事', '堇色素颜凉城空', 'ζ柔*陌殇丷', '落花人独立'];

    frameIndex = 0;

    playerId?: string;
    playerToken?: string;
    frameSyncExecutor?: FrameSyncExecutor;
    allPlayers: { [playerId: string]: PlayerData } = {};
    myPlayer?: PlayerComponent;
    tmpV2: Vec2 = v2();

    start() {
        var url = typeof (hallServerUrl) === "undefined" ? "http://127.0.0.1:7100/" : hallServerUrl;
        this.hallClient = globalThis.getHallClient(typeof (hallServerUrl) === "undefined" ? "http://127.0.0.1:7100/" : hallServerUrl);
        this.demoClient = globalThis.getDemoClient(typeof (demoServerUrl) === "undefined" ? "http://127.0.0.1:7901/" : demoServerUrl);

        this.stopGame();
        this.Joystick!.onmove = (dir) => {
            this.onJoysickMoveStart(dir);
        };
        this.Joystick!.onmoveend = () => {
            this.onJoysickMoveEnd();
        };

        this.viewStep(1);
        this.PlayerShowName.string = this.playerShowNameLibs[Math.floor(Math.random() * this.playerShowNameLibs.length)];
    }

    showTips(text: string, hideLoading = true) {
        this.ViewTipsLabel.string = text;
        this.ViewTips.active = true;
        if (hideLoading) this.hideLoading();
    }
    closeTips() {
        this.ViewTips.active = false;
    }

    showLoading(text?: string) {
        this.ViewLoadingText.string = text ?? '加载中，请稍后';
        this.ViewLoading.active = true;
    }
    hideLoading() {
        this.ViewLoading.active = false;
    }

    /**
     * 
     * @param step 1登录,2大厅,3房间,4游戏
     */
    viewStep(step: number) {
        this.ViewLogin.active = step == 1;
        this.ViewHall.active = step == 2;
        this.ViewRoom.active = step == 3 || step == 4;
        this.ViewRoomGameView.active = step == 4;
        this.ViewRoomBtnStartGame.active = step == 3;
        this.ViewRoomBtnStopGame.active = step == 4;
        this.ViewRoomBg.active = step == 3;
        this.ViewRoomShowPlayers.active = step == 3;
        //this.ViewRoomShowMsg.active = step == 3 || step == 4;
        this.ViewGame3D.active = step == 4;
    }

    async onLoginClick() {
        let showName = this.PlayerShowName.string;
        let openId = showName + Date.now();
        this.showLoading();
        //让接入的系统的服务端,请求TSGF获取玩家授权
        let result = await this.demoClient.playerAuth(openId, showName);
        this.hideLoading();
        if (!result.succ) {
            return this.showTips(result.err);
        }
        this.playerId = result.data.playerId;
        this.playerToken = result.data.playerToken;

        //进入大厅
        this.viewStep(2);
    }


    /**
     * 开始玩家匹配(基础混战),直到结果返回
     * @date 2022/5/16 - 17:47:46
     *
     * @protected
     * @async
     * @param {string[]} playerIds
     * @param {number} maxPlayers
     * @param {number} minPlayers
     * @returns {Promise<IResult<IMatchResult>>}
     */
    protected async startPlayersMatchBaseMelee(playerIds: string[], minPlayers: number, maxPlayers: number): Promise<IResult<IMatchResult>> {

        let reqMatchRet = await this.hallClient.requestMatch(this.playerToken!, {
            matchFromType: EMatchFromType.Player,
            matchFromInfo: {
                playerIds: playerIds,
            },
            maxPlayers: maxPlayers,
            matcherKey: 'BaseMelee',
            matcherParams: {
                minPlayers: minPlayers,
            } as IBaseMeleeMatcherParams,
        });
        if (!reqMatchRet.succ) {
            return Result.buildErr(reqMatchRet.err, reqMatchRet.code);
        }

        let matchResult: IMatchResult;
        do {
            await delay(1000);
            let reqResult = await this.hallClient.queryMatch(this.playerToken!, reqMatchRet.data);
            if (reqResult == null) {
                continue;
            }
            if (!reqResult.succ) {
                return reqResult;
            }
            matchResult = reqResult.data;
            break;
        } while (true);

        return Result.buildSucc(matchResult);
    }


    protected refreshPlayerNames(){
        this.ShowRoomPlayers.string = this.gameClient!.currRoomInfo!.playerList
            .map(p => p.showName + (p.networkState==ENetworkState.OFFLINE?'[离线]':''))
            .join(';   ');
    }
    /**
     * 根据游戏服务器地址,初始化并认证游戏客户端,成功则gameClient有值
     * @date 2022/5/16 - 18:01:34
     *
     * @protected
     * @async
     * @param {string} gameServerUrl
     * @returns {Promise<IResult<null>>}
     */
    protected async initAndAuthGameClient(gameServerUrl: string): Promise<IResult<null>> {
        this.gameClient = globalThis.getGameClient(this.playerToken!, gameServerUrl);
        let gameAuthRet = await this.gameClient.authorize();
        if (!gameAuthRet.succ) {
            this.gameClient = undefined;
            return Result.buildErr(gameAuthRet.err, gameAuthRet.code);
        }
        //初始化游戏客户端的相关事件
        this.gameClient.disconnectHandler = () => {
            //连接断开了,显示loading
            this.showLoading('断线重连中...');
            //返回true表示支持断线重连
            return true;
        }
        this.gameClient.onReconnectResult = (succ, err) => {
            this.hideLoading();
            //断线重连有结果了
            if (succ) {
                //成功连上,进入房间流程
                this.enterRoom();
            } else {
                //不再重连尝试了!表示彻底断开!(大多来自服务端的拒绝重试,超时之类)
                //退出房间到大厅
                this.exitRoom();
            }
            return;
        };
        this.gameClient.onPlayerJoinRoom = (player) => {
            this.appendRoomMsg(`[${player.showName}] 进入房间`);
            this.refreshPlayerNames();
        };
        this.gameClient.onPlayerLeaveRoom = (player) => {
            this.appendRoomMsg(`[${player.showName}] 离开房间`);
            this.refreshPlayerNames();
        };
        this.gameClient.onChangePlayerNetworkState= (player)=>{
            this.refreshPlayerNames();
        };
        this.gameClient.onDismissRoom = () => {
            this.appendRoomMsg(`房间被解散!`);
            this.exitRoom();
        };
        this.gameClient.onRecvRoomMsg = (roomMsg) => {
            let recvType = '';
            switch (roomMsg.recvType) {
                case ERoomMsgRecvType.ROOM_ALL:
                    recvType = '[所有人]';
                    break;
                case ERoomMsgRecvType.ROOM_OTHERS:
                    recvType = '[其他人]';
                    break;
                case ERoomMsgRecvType.ROOM_SOME:
                    recvType = '[你]';
                    break;
            }
            this.appendRoomMsg(`[${roomMsg.fromPlayerInfo.showName}] 对 ${recvType} 说:${roomMsg.msg}`);
        };
        this.gameClient.onStartFrameSync = (r, player) => {
            this.appendRoomMsg(`[${player.showName}] 开始了游戏!`);
            //开始
            this.onStartGame();
        };
        this.gameClient.onStopFrameSync = (r, player) => {
            this.appendRoomMsg(`[${player.showName}] 结束了游戏!`);
            //停止
            this.onStopGame();
        };

        this.frameSyncExecutor = new FrameSyncExecutor(
            new GameClientFrameSyncConnectAdp(this.gameClient!),
            'inputType',
            this,
            (dt, frameIndex) => this.executeOneFrame(dt, frameIndex),
            () => this.allPlayers);

        return Result.buildSucc(null);
    }

    /**
     * 由匹配进入游戏房间(成功则会初始化gameClient)
     * @date 2022/5/16 - 17:50:41
     *
     * @protected
     * @async
     * @param {IMatchResult} matchResult
     * @returns {Promise<IResult<IRoomInfo>>}
     */
    protected async matchEnterRoom(matchResult: IMatchResult): Promise<IResult<IRoomInfo>> {
        let authRet = await this.initAndAuthGameClient(matchResult.gameServerUrl);
        if (!authRet.succ) {
            return Result.buildErr(authRet.err, authRet.code);
        }
        //进入匹配结果的房间ID
        let joinRet = await this.gameClient!.joinRoom(matchResult.roomId);
        if (!joinRet.succ) {
            return joinRet;
        }
        return joinRet;
    }

    /**
     * 由创建进入游戏房间(成功则会初始化gameClient)
     * @date 2022/5/16 - 17:53:56
     *
     * @protected
     * @async
     * @returns {*}
     */
    protected async createRoomEnter(maxPlayers: number): Promise<IResult<IRoomInfo>> {
        let ret = await this.hallClient.createRoom(this.playerToken!, {
            roomName: '手动创建的房间',
            ownerPlayerId: this.playerId!,
            maxPlayers: maxPlayers,
            isPrivate: true,
        });
        if (!ret.succ) {
            return Result.buildErr(ret.err, ret.code);
        }

        let authRet = await this.initAndAuthGameClient(ret.data.gameServerUrl);
        if (!authRet.succ) {
            return Result.buildErr(authRet.err, authRet.code);
        }
        //进入创建好的房间ID
        let joinRet = await this.gameClient!.joinRoom(ret.data.roomInfo.roomId);
        if (!joinRet.succ) {
            return joinRet;
        }
        return joinRet;
    }
    /**
     * 由创建进入游戏房间(成功则会初始化gameClient)
     * @date 2022/5/16 - 17:53:56
     *
     * @protected
     * @async
     * @returns {*}
     */
    protected async joinRoomEnter(roomId: string): Promise<IResult<IRoomInfo>> {
        let ret = await this.hallClient.getRoomRegInfo(this.playerToken!, roomId);
        if (!ret.succ) {
            return Result.buildErr(ret.err, ret.code);
        }

        let authRet = await this.initAndAuthGameClient(ret.data.gameServerUrl);
        if (!authRet.succ) {
            return Result.buildErr(authRet.err, authRet.code);
        }
        //进入创建好的房间ID
        let joinRet = await this.gameClient!.joinRoom(roomId);
        if (!joinRet.succ) {
            return joinRet;
        }
        return joinRet;
    }

    async onCreateRoomClick() {
        this.showLoading();
        let enterRoomRet = await this.createRoomEnter(10);
        if (!enterRoomRet.succ) {
            return this.showTips(enterRoomRet.err);
        }
        //全部成功了,隐藏loading
        this.hideLoading();
        this.enterRoom();
    }
    async onJoinRoomClick() {
        this.showLoading();
        let enterRoomRet = await this.joinRoomEnter(this.InpJoinRoomId.string);
        if (!enterRoomRet.succ) {
            return this.showTips(enterRoomRet.err);
        }
        //全部成功了,隐藏loading
        this.hideLoading();
        this.enterRoom();
    }

    private async startBaseMeleeMatchEnterRoomFromCurrPlayer(minPlayers: number, maxPlayers: number): Promise<void> {
        this.showLoading(`匹配${minPlayers === maxPlayers ? minPlayers : (minPlayers + '~' + maxPlayers)}人房间中(无限等待)`);
        let matchResult = await this.startPlayersMatchBaseMelee([this.playerId!], minPlayers, maxPlayers);
        if (!matchResult.succ) {
            return this.showTips(matchResult.err);
        }
        let enterRoomRet = await this.matchEnterRoom(matchResult.data);
        if (!enterRoomRet.succ) {
            return this.showTips(enterRoomRet.err);
        }
        //全部成功了,隐藏loading
        this.hideLoading();
        this.enterRoom();
    }

    async onStartMatch1v1Click() {
        await this.startBaseMeleeMatchEnterRoomFromCurrPlayer(2, 2);
    }
    async onStartMatch2_20MeleeClick() {
        await this.startBaseMeleeMatchEnterRoomFromCurrPlayer(2, 20);
    }
    async onStartMatch1_10MeleeClick() {
        //就是现在没其他人匹配先创建房间先进,后面有其他人点了这种匹配,也进这个房间
        await this.startBaseMeleeMatchEnterRoomFromCurrPlayer(1, 10);
    }

    appendRoomMsg(msg: string) {
        this.ShowRoomMsg.string += `\r\n${msg}`;
        this.ShowRoomMsg.updateRenderData(true);
        this.ShowRoomMsgScrollView.scrollToBottom(0.3);
    }


    async onSendRoomMsgClick(event: Event, customEventData: string) {
        let editBox = this.InpRoomMsg;
        let msg = editBox.string;
        if (!msg) return;
        editBox.string = '';

        let ret = await this.gameClient!.sendRoomMsg({
            recvType: ERoomMsgRecvType.ROOM_ALL,
            msg: msg,
        });
        if (!ret.succ) {
            return this.showTips(ret.err);
        }
    }

    async onLeaveRoomClick() {
        await this.gameClient?.leaveRoom();
        await this.exitRoom();
    }
    async onDismissRoomClick() {
        await this.gameClient?.dismissRoom();
        await this.exitRoom();
    }

    /**请求已经进入房间了,这里执行进入房间流程(显示房间界面和信息,如果房间游戏已经开始会自动进入游戏流程)*/
    async enterRoom() {
        this.ShowRoomId.string = this.gameClient!.currRoomInfo!.roomId;
        this.ShowRoomPlayers.string = this.gameClient!.currRoomInfo!.playerList.map(p => p.showName).join(';   ');
        this.ShowRoomMsg.string = '';

        if (this.gameClient?.currRoomInfo?.ownerPlayerId === this.playerId) {
            //给房主显示解散房间按钮
            this.BtnDismissRoom.active = true;
            this.BtnLeaveRoom.active = false;
        } else {
            //其他人显示离开房间
            this.BtnDismissRoom.active = false;
            this.BtnLeaveRoom.active = true;
        }
        //进入房间界面
        this.viewStep(3);

        //判断房间的游戏状态
        if (this.gameClient!.currRoomInfo!.frameSyncState === EFrameSyncState.START) {
            //如果房间已经开始游戏了,则开始追帧
            let afterFramesRet = await this.gameClient!.requestAfterFrames();
            this.onStartGame(afterFramesRet.data!);
        }
    }
    /**请求数据都退出房间了, 这里执行退出房间流程, 回到大厅界面*/
    async exitRoom() {
        await this.stopGame();

        if (this.frameSyncExecutor) {
            await this.frameSyncExecutor?.dispose();
            this.frameSyncExecutor = undefined;
        }
        await this.gameClient?.disconnect();
        this.gameClient = undefined;

        this.viewStep(2);
    }

    async onStartGameClick() {
        //开始帧同步
        await this.gameClient?.startFrameSync();
    }
    async onExitGameClick() {
        await this.stopGame();
    }

    /**仅停止游戏,界面还在游戏中,玩家还在房间中*/
    async stopGame() {
        if (this.gameClient?.currRoomInfo?.frameSyncState === EFrameSyncState.START) {
            //如果在帧同步中,则先停止
            await this.gameClient?.stopFrameSync();
        }
    }

    async onStopGame() {
        this.viewStep(3);

        this.PlayersContainer?.removeAllChildren();
    }

    async onStartGame(afterFrames?: IAfterFrames) {

        this.PlayersContainer?.removeAllChildren();
        this.GameObjFlagUI?.removeAllChildren();

        this.frameSyncExecutor?.startExecuteFrame(this.gameClient!.currRoomInfo!.frameRate!, afterFrames);
        this.viewStep(4);
    }

    update(dt: number) {
    }
    lateUpdate() {
        if (this.myPlayer && this.myPlayer.isValid) {
            //更新摄像机
            this.MainCamera?.node.setPosition(
                this.myPlayer.node.position.x,
                this.myPlayer.node.position.y + 30,
                this.myPlayer.node.position.z + 40);
            this.MainCamera?.node.lookAt(this.myPlayer.node.position);
        }
    }

    onNewPlayer(playerId: string, playerInfo: IPlayerInfo, dt: number): void {
        const p = new PlayerData();
        p.playerId = playerId;
        p.showName = playerInfo.showName;
        this.allPlayers[playerId] = p;

        var newPlayerName = instantiate(this.PlayerNameFlagPrefab!);
        newPlayerName.name = "PlayerNameId_" + playerId;
        let newPlayerNameFlag = newPlayerName.getComponent(PlayerNameFlagComponent)!;
        this.GameObjFlagUI?.addChild(newPlayerName);
        var newPlayer = instantiate(this.PlayerPrefab!);
        newPlayer.name = "PlayerId_" + playerId;
        var playerComp = newPlayer.getComponent(PlayerComponent)!;
        playerComp.init(p, this.MainCamera, newPlayerName);
        playerComp.playerNameFlag = newPlayerNameFlag;
        this.PlayersContainer?.addChild(newPlayer);

        if (playerId == this.gameClient?.playerId) {
            this.myPlayer = playerComp;
        }
    }
    onRemovePlayer(playerId: string, dt: number): void {
        const p = this.allPlayers[playerId];
        delete this.allPlayers[playerId];

        var playerNode = this.PlayersContainer?.getChildByName("PlayerId_" + p.playerId);
        let comp = playerNode?.getComponent(PlayerComponent);
        comp?.playerNameFlag.node.parent?.removeChild(comp?.playerNameFlag.node);
        if (playerNode) this.PlayersContainer?.removeChild(playerNode);
        playerNode?.destroy();
    }


    execInputOthers(playerId: string, inputFrame: IFramePlayerInput, dt: number, FrameIndex: number) {
        switch (inputFrame.inputFrameType) {
            case EPlayerInputFrameType.PlayerEnterGame:
                //开始游戏时,房间中的玩家都触发一次
                this.onNewPlayer(playerId, inputFrame.playerInfo, dt);
                break;
            case EPlayerInputFrameType.JoinRoom:
                //游戏开始后再加入的玩家
                this.onNewPlayer(playerId, inputFrame.playerInfo, dt);
                break;
            case EPlayerInputFrameType.LeaveRoom:
                //游戏开始后再离开的玩家
                this.onRemovePlayer(playerId, dt);
                break;
        }
    }
    execInputOperates_MoveDirStart(playerId: string, inputFrame: IPlayerInputOperate, dt: number): void {
        const player = this.allPlayers[playerId] as PlayerData;
        if (!player) return;
        player.inMoving = true;
        player.moveSignRadFromX = inputFrame.signRadFromX;
        player.lastDir.set(Vec2.UNIT_X);
        player.lastDir.rotate(player.moveSignRadFromX);
    }
    execInputOperates_MoveDirEnd(playerId: string, inputFrame: IPlayerInputOperate, dt: number): void {
        const player = this.allPlayers[playerId];
        if (!player) return;
        player.inMoving = false;
    }


    executeOneFrame(dt: number, frameIndex: number): void {
        this.frameIndex = frameIndex;
        for (var playerId in this.allPlayers) {
            var p = this.allPlayers[playerId];
            this.playerUpdate(p, dt);
        }
    }
    playerUpdate(player: PlayerData, dt: number) {
        if (player.inMoving) {
            //有移动,转向直接生效,小方块,懒得转忽略
            var distance = player.speed * dt;//本帧移动的距离
            //根据方向,算出本帧移动向量
            Vec2.multiplyScalar(this.tmpV2, player.lastDir, distance);
            //加到老坐标
            Vec2.add(player.pos, player.pos, this.tmpV2);
        }
    }


    onJoysickMoveStart(move: IMoveDirection) {
        this.gameClient?.playerInpFrame([
            {
                inputType: InputType.MoveDirStart,
                signRadFromX: move.signRadFromX,
            }
        ]);
    }
    onJoysickMoveEnd() {
        this.gameClient?.playerInpFrame([
            {
                inputType: InputType.MoveDirEnd,
            }
        ]);
    }
}